﻿using Common;
using Common.FrontEnd;
using FrontEnd;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace Linguist.Acoustic.Tiedstate
{
    /// <summary>
    /// Represents a composite senone. A composite senone consists of a set of all possible {@link Senone senones} for a
    /// given state. CompositeSenones are used when the exact context of a senone is not known. The CompositeSenone
    /// represents all the possible senones.
    ///
    /// This class currently only needs to be public for testing purposes.
    /// 
    /// Note that all scores are maintained in LogMath log base
    /// </summary>
    public class CompositeSenone : ScoreCachingSenone
    {
        private static int MAX_SENONES = 20000;
        private static Boolean wantMaxScore = true;
        private ISenone[] senones;
        private float weight;

        /**
        /// a factory method that creates a CompositeSenone from a list of senones.
         *
        /// @param senoneCollection the Collection of senones
        /// @return a composite senone
         */
        public static CompositeSenone create(ICollection<ISenone> senoneCollection,
                                             float weight) 
        {
            return new CompositeSenone(senoneCollection.ToArray(), weight);
        }


        /**
        /// Constructs a CompositeSenone given the set of constituent senones
         *
        /// @param senones the set of constituent senones
         */
        public CompositeSenone(ISenone[] senones, float weight) 
        {
            this.senones = senones;
            this.weight = weight;
            Trace.Write(" " + senones.Length.ToString());
        }


        /**
        /// Dumps this senone
         *
        /// @param msg annotation for the dump
         */
        public void dump(String msg) 
        {
            Trace.WriteLine("   CompositeSenone " + msg + ": ");
            foreach (ISenone senone in senones) 
            {
                senone.dump("   ");
            }
        }


        /**
        /// Calculates the composite senone score. Typically this is the best score for all of the constituent senones
         */
        override public float calculateScore(IData feature) 
        {
            float logScore;
            if (wantMaxScore) 
            {
                logScore = -float.MaxValue;
                foreach (ISenone senone in senones) 
                {
                    logScore = Math.Max(logScore, senone.getScore(feature));
                }
            } 
            else 
            { // average score
                logScore = 0.0f;
                foreach (ISenone senone in senones) 
                {
                    logScore += senone.getScore(feature);
                }
                logScore = logScore / senones.Length;
            }
            return logScore + weight;
        }


        /**
        /// Calculate scores for each component in the senone's distribution. Not yet implemented.
         *
        /// @param feature the current feature
        /// @return the score for the feature in LogMath
         */
        public float[] calculateComponentScore(IData feature) 
        {
            Trace.Assert(false, "Not implemented!");
            return null;
        }


        /**
        /// Returns the set of senones that compose this composite senone. This method is only needed for unit testing.
         *
        /// @return the array of senones.
         */
        public ISenone[] getSenones() 
        {
            return senones;
        }


        /**
        /// Determines if two objects are equal
         *
        /// @param o the object to compare to this.
        /// @return true if the objects are equal
         */
        
        public Boolean Equals(Object o) 
        {
            if (!(o is ISenone)) 
            {
                return false;
            }
            ISenone other = (ISenone) o;
            return this.getID() == other.getID();
        }


        /**
        /// Returns the hashcode for this object
         *
        /// @return the hashcode
         */
        public int hashCode() 
        {
            long id = getID();
            int high = (int) ((id >> 32));
            int low = (int) (id);
            return high + low;
        }


        /**
        /// Gets the ID for this senone
         *
        /// @return the senone id
         */
        public long getID() 
        {
            long factor = 1L;
            long id = 0L;
            foreach (ISenone senone in senones) 
            {
                id += senone.getID()* factor;
                factor = factor* MAX_SENONES;
            }
            return id;
        }


        /**
        /// Retrieves a string form of this object
         *
        /// @return the string representation of this object
         */
        override
        public String ToString() 
        {
            return "senone id: " + getID();
        }

	    public MixtureComponent[] getMixtureComponents() {
		    return null;
	    }


	    public float[] getLogMixtureWeights() {
		    return null;
	    }
    }
}
